﻿namespace itau.im.gerador
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Text.RegularExpressions;
    using itau.im.entidade;
    using itau.im.negocio;
    using EnterpriseLibraryWrapper;
    using System.Threading;
    using itau.im.interpolador;

    public class GeradorDeCurva : IGeradorDeCurva
    {

        #region Constantes

        private const int CURVA_TIPO_ENTRADA_COMPOSTA = 1;
        private const int CURVA_TIPO_ENTRADA_MANUAL = 2;
        private const int CURVA_TIPO_ENTRADA_IMPORTACAO_ARQUIVO = 3;

        private const int NAO = 0;
        private const int SIM = 1;

        private const int SITUACAO_EXECUCAO_AGENDADA = 803;
        private const int SITUACAO_EXECUCAO_MANUAL = 800;
        private const int SITUACAO_EXECUCAO_SOLICITADA_UPLOAD = 14;
        private const int SITUACAO_EXECUCAO_EM_EXECUCAO = 500;
        private const int SITUACAO_EXECUCAO_AGUARDANDO_LIBERACAO = 17;
        private const int SITUACAO_EXECUCAO_RETIDA = 20;
        private const int SITUACAO_EXECUCAO_LIBERADA = 40;
        private const int SITUACAO_EXECUCAO_RETIRAR_AGENDAMENTO = 99;
        private const int SITUACAO_EXECUCAO_RETIRAR_AGENDAMENTO_PRICING = 100;

        private const int TIPO_TAXA_EXPONENCIAL = 1;
        private const int TIPO_TAXA_LINEAR = 2;
        private const int TIPO_TAXA_FATOR_DIARIO = 3;
        private const int TIPO_TAXA_PU = 4;
        private const int TIPO_TAXA_CONSTANTE = 5;
        private const int TIPO_TAXA_CUBIC_SPLINE = 6;

        private const int TIPO_PONTO_ANTEPOLACAO = 1;
        private const int TIPO_PONTO_INTERPOLACAO = 2;
        private const int TIPO_PONTO_EXTRAPOLACAO = 3;
        private const int TIPO_PONTO_VERTICE = 4;
        private const int TIPO_PONTO_TAXA = 5;

        private const int CURVA_STATUS_ATIVA = 1;
        private const int CURVA_STATUS_PENDENTE = 2;
        private const int CURVA_STATUS_INATIVA = 3;

        private const int CURVA_TIPO_TAXA = 1;
        private const int CURVA_TIPO_SPREAD = 2;

        private const int PRACA_CALENDARIO_BRASIL = 1;
        private const int MODELO_CALCULO_GENERICO = 5;

        private const int CODIGO_TIPO_CURVA_PRICING = 2;

        private const int INDICADOR_VERTICE_NAO = 0;
        private const int INDICADOR_VERTICE_SIM = 1;

        private const int TIPO_COMPOSICAO_CURVA = 1;
        private const int TIPO_COMPOSICAO_COTACAO = 2;
        private const int COD_TIPO_PRAZO_BASE_DU_252 = 1;
        private const int COD_TIPO_PRAZO_BASE_DC_360 = 2;
        private const int COD_TIPO_PRAZO_BASE_DC_30 = 3;
        private const int COD_TIPO_PRAZO_BASE_OVER = 4;
        private const string MASCARA_DATA = "yyyy-MM-dd HH:mm:ss.ffffff";

        #endregion

        #region Atributos

        ICurvaBS curvaBS;
        IList<CurvaExecucaoPonto> listaVertices = null;
        IImportadorArquivoVertices importArquivoVertices;
        IExecucaoCurvaBS execucaoCurvaBS;
        ICalendarioBS calendario;
        IConversorTaxas conversorTaxas;
        ImportacaoArquivo importArquivo;
        Dictionary<string, IList<CurvaExecucaoPonto>> dadosCalculados;
        Dictionary<string, string> log;

        #endregion

        #region Propriedades

        public List<Item> MapaCofiguracoesGerador
        {
            get;
            set;
        }

        public RegistroExecucaoCurva RegistroExecucaoCurva
        {
            get;
            set;
        }

        public Curva Curva
        {
            get;
            set;
        }

        private IList<CurvaVigente> ListaCurvasFatorVigenteComPontos
        {
            get;
            set;
        }

        private ComposicaoCurva ComposicaoCurvaComposta
        {
            get;
            set;
        }

        private ConfiguracoesBS ConfiguracoesBS
        {
            get;
            set;
        }

        private int SituacaoAtual
        {
            get;
            set;
        }

        #endregion

        #region Construtor

        public GeradorDeCurva()
        {
            conversorTaxas = new ConversorTaxasBS();
            execucaoCurvaBS = new ExecucaoCurvaBS();
            importArquivoVertices = new ImportadorArquivoVertices();
            curvaBS = new CurvaBS();
            calendario = new CalendarioBS();
            dadosCalculados = new Dictionary<string, IList<CurvaExecucaoPonto>>();
            log = new Dictionary<string, string>();
        }

        #endregion

        #region Destrutor

        ~GeradorDeCurva()
        {
            conversorTaxas = null;
            execucaoCurvaBS = null;
            importArquivoVertices = null;
            curvaBS = null;
            calendario = null;
            dadosCalculados = null;
        }

        #endregion

        #region Métodos Públicos

        public void Gerar()
        {
            try
            {
                GerarCurvaBullet();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        #endregion

        #region Métodos Privados

        private void GerarCurvaBullet()
        {
            try
            {
                System.Diagnostics.Debug.WriteLine(string.Format("Codigo {0}, Data Exec {1}, Data {2} Inicio", RegistroExecucaoCurva.CodigoCurva, RegistroExecucaoCurva.DataHoraExecucaoCurva.ToString("dd/MM/yyyy HH:mm:ss.ffffff"), System.DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss.ffffff")));

                AtualizarSituacaoExecucaoCurvaParaEmExecucao();

                ManterUmaExecucaoPrincingPorDia();

                ObterVertices();

                CalcularPontos();

                System.Diagnostics.Debug.WriteLine(string.Format("Codigo {0}, Data Exec {1}, Data {2} Fim", RegistroExecucaoCurva.CodigoCurva, RegistroExecucaoCurva.DataHoraExecucaoCurva.ToString("dd/MM/yyyy HH:mm:ss.ffffff"), System.DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss.ffffff")));
            }
            catch (Exception ex)
            {
                LimparSujeiraExecucao();
                System.Diagnostics.Debug.WriteLine(string.Format("Codigo {0}, Data Exec {1}, Data {2} Erro", RegistroExecucaoCurva.CodigoCurva, RegistroExecucaoCurva.DataHoraExecucaoCurva.ToString("dd/MM/yyyy HH:mm:ss.ffffff"), System.DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss.ffffff")));
                Exception exCurva = new Exception(string.Format("FIM-COD_CURV:{0};DT_INCU_VIGE:{1};DAT_HOR_EXEO_CURV:{2};NOM_CURV:{3};Falha:{4}", RegistroExecucaoCurva.CodigoCurva, RegistroExecucaoCurva.DataInclusaoVigencia.ToString(MASCARA_DATA), RegistroExecucaoCurva.DataHoraExecucaoCurva.ToString(MASCARA_DATA), Curva.NomeCurva, ex.Message), ex);
                EnterpriseLibraryFactory.GetExceptionHandler().HandleException(exCurva, "ErrosGerador");
            }
        }

        private void ValidarSeExecucaoEstahValida()
        {
            if (Curva.CurvaVigente.CodigoTipoEntradaDado == CURVA_TIPO_ENTRADA_COMPOSTA
                && RegistroExecucaoCurva.CodigoSituacao == SITUACAO_EXECUCAO_AGENDADA)
            {
                ComposicaoCurva composicaoCurvaComposta;
                IList<Curva> listaCurvasFator = new List<Curva>();
                IList<CurvaVigente> listaCurvasFatorVigenteComPontos;
                PricingBS pricingBS = new PricingBS();
                Curva curva = null;
                composicaoCurvaComposta = curvaBS.ObterCurvaComposicaoVigente(RegistroExecucaoCurva.CodigoCurva, RegistroExecucaoCurva.DataInclusaoVigencia);

                for (int cont = 0; cont < composicaoCurvaComposta.Itens.Count; cont++)
                {
                    if (composicaoCurvaComposta.Itens[cont].CodigoTipoComposicao.Equals(TIPO_COMPOSICAO_CURVA))
                    {
                        curva = new Curva();
                        curva.CodigoCurva = composicaoCurvaComposta.Itens[cont].CodigoCurvaIntegrante;
                        listaCurvasFator.Add(curva);
                    } 
                    else if (composicaoCurvaComposta.Itens[cont].CodigoTipoComposicao.Equals(TIPO_COMPOSICAO_COTACAO))
                    {
                        string valorAtributo = pricingBS.ObterValorSerieAtributo(composicaoCurvaComposta.Itens[cont].CodigoSerie, composicaoCurvaComposta.Itens[cont].CodigoAtributo, RegistroExecucaoCurva.DataHoraExecucaoCurva.AddDays(composicaoCurvaComposta.Itens[cont].CodigoSerieIntegrante));

                        if (valorAtributo.Equals(string.Empty))
                        {
                            throw new Exception(string.Format("Não foi possível obter a paridade desta curva. Código série: {0}, Código atributo: {1} e Data execução: {2}.", composicaoCurvaComposta.Itens[cont].CodigoSerie, composicaoCurvaComposta.Itens[cont].CodigoAtributo, RegistroExecucaoCurva.DataHoraExecucaoCurva));
                        }
                    }
                }

                listaCurvasFatorVigenteComPontos = execucaoCurvaBS.ObterUltimaExecucaoCurvasDataReferencia(listaCurvasFator, RegistroExecucaoCurva.DataHoraExecucaoCurva.Date.AddHours(23).AddMinutes(59).AddSeconds(59), System.Configuration.ConfigurationManager.AppSettings["ProcedureConsulta"]);

                if (listaCurvasFatorVigenteComPontos.Count != listaCurvasFator.Count) 
                {
                    throw new Exception(string.Format("Não foi possível obter todas as execuções das curvas base da composta. Codigo da curva: {0} e data de execução: {1}.", RegistroExecucaoCurva.CodigoCurva, RegistroExecucaoCurva.DataHoraExecucaoCurva));
                }

                foreach (CurvaVigente curvaVigente in listaCurvasFatorVigenteComPontos) 
                {
                    if (!(curvaVigente.RegistroExecucaoCurva != null && curvaVigente.RegistroExecucaoCurva.Count > 0))
                    {
                        throw new Exception(string.Format("Não foi possível obter todas as execuções das curvas base da composta. Codigo da curva: {0} e data de execução: {1}.", RegistroExecucaoCurva.CodigoCurva, RegistroExecucaoCurva.DataHoraExecucaoCurva));
                    }
                }
            }
        }

        private void ManterUmaExecucaoPrincingPorDia()
        {
            if (Curva.CodigoTipoCurva.Equals(CODIGO_TIPO_CURVA_PRICING)) 
            {
                execucaoCurvaBS.AtualizarSituacaoRegistrosExecucaoCurva(RegistroExecucaoCurva.CodigoCurva, RegistroExecucaoCurva.DataHoraExecucaoCurva.Date, SITUACAO_EXECUCAO_LIBERADA, SITUACAO_EXECUCAO_RETIRAR_AGENDAMENTO_PRICING);
                execucaoCurvaBS.AtualizarSituacaoRegistrosExecucaoCurva(RegistroExecucaoCurva.CodigoCurva, RegistroExecucaoCurva.DataHoraExecucaoCurva.Date, SITUACAO_EXECUCAO_RETIDA, SITUACAO_EXECUCAO_RETIRAR_AGENDAMENTO_PRICING);
                execucaoCurvaBS.AtualizarSituacaoRegistrosExecucaoCurva(RegistroExecucaoCurva.CodigoCurva, RegistroExecucaoCurva.DataHoraExecucaoCurva.Date, SITUACAO_EXECUCAO_AGUARDANDO_LIBERACAO, SITUACAO_EXECUCAO_RETIRAR_AGENDAMENTO_PRICING);
            }
        }

        private void AgendarPMTAssociadas()
        {
            try
            {
                IList<CurvaVigente> listaPMTs = curvaBS.ObterCurvasVigentesPMT(Curva.CodigoCurva);

                if (listaPMTs != null && listaPMTs.Count > 0)
                {
                    foreach (CurvaVigente curvaVigente in listaPMTs)
                    {
                        RegistroExecucaoCurva registroExecucaoPMT = new RegistroExecucaoCurva();
                        registroExecucaoPMT.CodigoCurva = curvaVigente.CodigoCurvaVigente;
                        registroExecucaoPMT.DataInclusaoVigencia = curvaVigente.DataInclusao;
                        registroExecucaoPMT.CodigoSituacao = SITUACAO_EXECUCAO_AGENDADA;
                        registroExecucaoPMT.DataHoraExecucaoCurva = RegistroExecucaoCurva.DataHoraExecucaoCurva;
                        FilaInclusaoPMT.Adicionar(registroExecucaoPMT);
                    }
                }
            }
            catch { }
        }

        private void LimparSujeiraExecucao()
        {
            RegistroExecucaoCurva.DataHoraLiberacaoCurva = DateTime.MinValue;
            FilaExcecao.Adicionar(RegistroExecucaoCurva);
        }

        private void AtualizarSituacaoExecucaoCurvaParaEmExecucao()
        {
            AlterarSituacaoDaExecucao(SITUACAO_EXECUCAO_EM_EXECUCAO + RegistroExecucaoCurva.CodigoSituacao);
            RegistroExecucaoCurva.CodigoSituacao = RegistroExecucaoCurva.CodigoSituacao - SITUACAO_EXECUCAO_EM_EXECUCAO;
        }

        private void AlterarSituacaoDaExecucao(int codigoSituacao)
        {
            RegistroExecucaoCurva.CodigoSituacao = codigoSituacao;
            execucaoCurvaBS.AtualizarRegistroExecucaoCurva(RegistroExecucaoCurva);
        }

        private void ObterVertices()
        {
            ObterVerticesComposta();

            ObterVerticesImportacao();

            ObterVerticesManual();

            VerificarSeVieramVertices();
        }

        private void ObterVerticesImportacao()
        {
            if (Curva.CurvaVigente.CodigoTipoEntradaDado == CURVA_TIPO_ENTRADA_IMPORTACAO_ARQUIVO
                 && (RegistroExecucaoCurva.CodigoSituacao == SITUACAO_EXECUCAO_AGENDADA 
                 || RegistroExecucaoCurva.CodigoSituacao == SITUACAO_EXECUCAO_SOLICITADA_UPLOAD))
            {
                importArquivo = curvaBS.ObterDadosArquivoImportacao(Curva.CurvaVigente.CodigoCurvaVigente, Curva.CurvaVigente.DataInclusao);
                
                try
                {
                    VerificarDataBaseArquivo();

                    listaVertices = importArquivoVertices.ImportarVertices(importArquivo, RegistroExecucaoCurva);
                    execucaoCurvaBS.CriarCurvaExecucaoPontos(listaVertices, System.Configuration.ConfigurationManager.AppSettings["Procedure"]);
                }
                catch (Exception ex)
                {
                    Exception exCustom = new Exception(string.Format("Falha no processamento do arquivo, na curva de código: {0} e nome: {1}.", RegistroExecucaoCurva.CodigoCurva, Curva.NomeCurva), ex);
                    EnterpriseLibraryFactory.GetExceptionHandler().HandleException(exCustom, "ErrosDados");
                }
            }
        }

        private void ObterVerticesManual()
        {
            if (Curva.CurvaVigente.CodigoTipoEntradaDado == CURVA_TIPO_ENTRADA_MANUAL 
                && RegistroExecucaoCurva.CodigoSituacao == SITUACAO_EXECUCAO_AGENDADA)
            {
                SituacaoAtual = SITUACAO_EXECUCAO_RETIRAR_AGENDAMENTO;
                throw new Exception(string.Format("Curva manual não é executada pelo agendador. Código da curva: {0} e nome: {1}", RegistroExecucaoCurva.CodigoCurva, Curva.NomeCurva));
            }
            else if ((Curva.CurvaVigente.CodigoTipoEntradaDado == CURVA_TIPO_ENTRADA_MANUAL)
                        || (Curva.CurvaVigente.CodigoTipoEntradaDado == CURVA_TIPO_ENTRADA_IMPORTACAO_ARQUIVO
                            && RegistroExecucaoCurva.CodigoSituacao == SITUACAO_EXECUCAO_MANUAL))
            {
                listaVertices = execucaoCurvaBS.ObterVerticesCurvaVigente(RegistroExecucaoCurva.CodigoCurva, RegistroExecucaoCurva.DataInclusaoVigencia, RegistroExecucaoCurva.DataHoraExecucaoCurva);
            }
        }

        private void ObterVerticesComposta()
        {
            if (Curva.CurvaVigente.CodigoTipoEntradaDado == CURVA_TIPO_ENTRADA_COMPOSTA
                && RegistroExecucaoCurva.CodigoSituacao == SITUACAO_EXECUCAO_AGENDADA)
            {
                ComposicaoCurva composicaoCurvaComposta;
                IList<Curva> listaCurvasFator = new List<Curva>();
                IList<CurvaVigente> listaCurvasFatorVigenteComPontos;
                Curva curva;
                PricingBS pricingBS = new PricingBS();

                int prazoCurvaComposta;

                composicaoCurvaComposta = curvaBS.ObterCurvaComposicaoVigente(RegistroExecucaoCurva.CodigoCurva, RegistroExecucaoCurva.DataInclusaoVigencia);

                for (int cont = 0; cont < composicaoCurvaComposta.Itens.Count; cont++)
                {
                    if (composicaoCurvaComposta.Itens[cont].CodigoTipoComposicao.Equals(TIPO_COMPOSICAO_CURVA))
                    {
                        curva = new Curva();
                        curva.CodigoCurva = composicaoCurvaComposta.Itens[cont].CodigoCurvaIntegrante;
                        listaCurvasFator.Add(curva);
                    }
                    else if (composicaoCurvaComposta.Itens[cont].CodigoTipoComposicao.Equals(TIPO_COMPOSICAO_COTACAO))
                    {
                        string valorAtributo = pricingBS.ObterValorSerieAtributo(composicaoCurvaComposta.Itens[cont].CodigoSerie, composicaoCurvaComposta.Itens[cont].CodigoAtributo, RegistroExecucaoCurva.DataHoraExecucaoCurva.AddDays(composicaoCurvaComposta.Itens[cont].CodigoSerieIntegrante));

                        if (valorAtributo.Equals(string.Empty))
                        {
                            throw new Exception(string.Format("Não foi possível obter a paridade desta curva. Código série: {0}, Código atributo: {1} e Data execução: {2}.", composicaoCurvaComposta.Itens[cont].CodigoSerie, composicaoCurvaComposta.Itens[cont].CodigoAtributo, RegistroExecucaoCurva.DataHoraExecucaoCurva));
                        }

                        composicaoCurvaComposta.FormulaComposicao = composicaoCurvaComposta.FormulaComposicao.Replace(composicaoCurvaComposta.Itens[cont].OperandoFormula, FormatarValorSerieAtributo(valorAtributo));
                    }
                }

                listaCurvasFatorVigenteComPontos = execucaoCurvaBS.ObterUltimaExecucaoCurvasDataReferencia(listaCurvasFator, RegistroExecucaoCurva.DataHoraExecucaoCurva.Date.AddHours(23).AddMinutes(59).AddSeconds(59), System.Configuration.ConfigurationManager.AppSettings["ProcedureConsulta"]);

                prazoCurvaComposta = curvaBS.ObterMinimoAnosExtensaoCurvas(listaCurvasFator);

                listaVertices = CalcularCurvaComposta(composicaoCurvaComposta, prazoCurvaComposta, listaCurvasFatorVigenteComPontos, Curva.CurvaVigente, RegistroExecucaoCurva);

            
                for (int i = 0; i < listaVertices.Count; i++)
                {
                    CurvaExecucaoPonto pontoCurva = listaVertices[i];

                    pontoCurva.CodigoCurva = RegistroExecucaoCurva.CodigoCurva;
                    pontoCurva.DataHoraExecucaoCurva = RegistroExecucaoCurva.DataHoraExecucaoCurva;
                    pontoCurva.DataInclusaoVigencia = RegistroExecucaoCurva.DataInclusaoVigencia;
                    pontoCurva.IndicadorVertice = INDICADOR_VERTICE_NAO;
                }

                IList<TaxaPontoCurvaVigente> listaMetodosInterpolacao = curvaBS.ObterMetodosInterpolacaoCurva(Curva.CurvaVigente.CodigoCurvaVigente, Curva.CurvaVigente.DataInclusao);
                IList<TipoPrazo> tiposPrazo;

                Curva.CurvaVigente.lstTaxaPontoCurvaVigente = listaMetodosInterpolacao;
                tiposPrazo = curvaBS.ObterTiposPrazo();

                int antepolacaoID = 0;
                int interpolacaoID = 0;
                int extrapolacaoID = 0;
                int tipoVerticeInterpolacoID = 0;

                for (int i = 0; i < Curva.CurvaVigente.lstTaxaPontoCurvaVigente.Count; i++)
                {
                    if (Curva.CurvaVigente.lstTaxaPontoCurvaVigente[i].CodigoTipoPontoCurva.Equals(TIPO_PONTO_ANTEPOLACAO))
                    {
                        antepolacaoID = Curva.CurvaVigente.lstTaxaPontoCurvaVigente[i].CodigoTipoTaxa;
                    }
                    else if (Curva.CurvaVigente.lstTaxaPontoCurvaVigente[i].CodigoTipoPontoCurva.Equals(TIPO_PONTO_INTERPOLACAO))
                    {
                        interpolacaoID = Curva.CurvaVigente.lstTaxaPontoCurvaVigente[i].CodigoTipoTaxa;
                    }
                    else if (Curva.CurvaVigente.lstTaxaPontoCurvaVigente[i].CodigoTipoPontoCurva.Equals(TIPO_PONTO_EXTRAPOLACAO))
                    {
                        extrapolacaoID = Curva.CurvaVigente.lstTaxaPontoCurvaVigente[i].CodigoTipoTaxa;
                    }
                    else if (Curva.CurvaVigente.lstTaxaPontoCurvaVigente[i].CodigoTipoPontoCurva.Equals(TIPO_PONTO_VERTICE))
                    {
                        tipoVerticeInterpolacoID = Curva.CurvaVigente.lstTaxaPontoCurvaVigente[i].CodigoTipoTaxa;
                    }
                }

                var tipoPrazo = (from t in tiposPrazo
                                 where t.Codigo == Curva.CurvaVigente.CodigoTipoPrazo
                                 select t).ToList();

                int baseDias = 0;
                if (tipoPrazo.Count > 0)
                {
                    baseDias = tipoPrazo[0].QuantidadeDiasAno;
                }

                Interpolador interpoladorBS = new Interpolador();
                interpoladorBS.AjustarTaxaParaPontosEmDiasNaoUteis(ref listaVertices, Curva.CurvaVigente.CodigoModeloCalculo, tipoVerticeInterpolacoID, interpolacaoID, baseDias);

                execucaoCurvaBS.CriarCurvaExecucaoPontos(listaVertices, System.Configuration.ConfigurationManager.AppSettings["Procedure"]);

                execucaoCurvaBS.ValidarLiberacaoCurva(RegistroExecucaoCurva, Curva, true);
            }
        }

        private void AjustarTaxaParaPontosEmDiasNaoUteis(ref IList<CurvaExecucaoPonto> listaVertices) 
        {
            int duAnt = 0;
            if (Curva.CurvaVigente.CodigoModeloCalculo == 6)
            {
                for (int i = 0; i < listaVertices.Count; i++)
                {
                    CurvaExecucaoPonto pontoCurva = listaVertices[i];

                    //Se quantidade de dias uteis é igual à quantidade de dias uteis anterior, então não é um dia util. 
                    if (pontoCurva.QuantidadeDiasUteis == duAnt && duAnt != 0)
                    {
                        for (int j = i + 1; j < listaVertices.Count; j++)
                        {
                            if (listaVertices[j].QuantidadeDiasUteis == duAnt + 1)
                            {
                                duAnt = pontoCurva.QuantidadeDiasUteis;
                                pontoCurva.QuantidadeDiasUteis = listaVertices[j].QuantidadeDiasUteis;
                                pontoCurva.ValorVertice = listaVertices[j].ValorVertice;
                                break;
                            }
                        }
                    }
                    else
                    {
                        duAnt = pontoCurva.QuantidadeDiasUteis;
                    }
                }
            }
        }

        private string FormatarValorSerieAtributo(string valorAtributo)
        {
            double valor = Convert.ToDouble(valorAtributo.Replace(".", ","));

            if (valor > 0)
            {
                return valor.ToString("N99");
            }
            else 
            {
                return (valor * -1).ToString("N99");
            }
        }

        private void VerificarSeVieramVertices()
        {
            if (listaVertices.Count == 0)
            {
                System.Diagnostics.Debug.WriteLine(string.Format("Codigo {0}, Data Exec {1}, Data {2} Sem vértice", RegistroExecucaoCurva.CodigoCurva, RegistroExecucaoCurva.DataHoraExecucaoCurva.ToString("dd/MM/yyyy HH:mm:ss.ffffff"), System.DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss.ffffff")));
                throw new Exception(string.Format("Não vieram vértices para a curva. Código da curva: {0}. Data inclusão vigência: {1}. Data hora execução: {2}", RegistroExecucaoCurva.CodigoCurva, RegistroExecucaoCurva.DataInclusaoVigencia, RegistroExecucaoCurva.DataHoraExecucaoCurva));
            }
        }

        private bool CurvasPrimariasForamExecutadasHoje(IList<CurvaVigente> listaCurvasFatorVigenteComPontos)
        {
            if (listaCurvasFatorVigenteComPontos != null && listaCurvasFatorVigenteComPontos.Count > 0)
            {
                foreach (CurvaVigente curvaVigente in listaCurvasFatorVigenteComPontos)
                {
                    RegistroExecucaoCurva registroExecucaoCurva = curvaVigente.RegistroExecucaoCurva[0];

                    if (!registroExecucaoCurva.DataHoraExecucaoCurva.Date.Equals(DateTime.Now.Date))
                    {
                        return false;
                    }
                }
            }
            else
            {
                return false;
            }

            return true;
        }

        private void CalcularPontos()
        {
            IConversorTaxas conversor = new ConversorTaxasBS();
            int dataMaxGeracaoCurva;
            int baseDias = 0;

            if (Curva.CurvaVigente.CodigoTipoEntradaDado != CURVA_TIPO_ENTRADA_COMPOSTA)
            {
                IList<TaxaPontoCurvaVigente> listaMetodosInterpolacao = curvaBS.ObterMetodosInterpolacaoCurva(Curva.CurvaVigente.CodigoCurvaVigente, Curva.CurvaVigente.DataInclusao);
                IList<TipoPrazo> tiposPrazo;
                IList<TaxaPontoCurvaVigente> taxa;

                Curva.CurvaVigente.lstTaxaPontoCurvaVigente = listaMetodosInterpolacao;
                tiposPrazo = curvaBS.ObterTiposPrazo();

                taxa = ConverterVertices(conversor, tiposPrazo);

                dataMaxGeracaoCurva = curvaBS.ObterMaximoExtensaoDiasCurva(Curva.CurvaVigente.CodigoCurvaVigente);
                int antepolacaoID = 0;
                int interpolacaoID = 0;
                int extrapolacaoID = 0;
                int tipoVerticeInterpolacoID = 0;

                for (int i = 0; i < Curva.CurvaVigente.lstTaxaPontoCurvaVigente.Count; i++)
                {
                    if (Curva.CurvaVigente.lstTaxaPontoCurvaVigente[i].CodigoTipoPontoCurva.Equals(TIPO_PONTO_ANTEPOLACAO))
                    {
                        antepolacaoID = Curva.CurvaVigente.lstTaxaPontoCurvaVigente[i].CodigoTipoTaxa;
                    }
                    else if (Curva.CurvaVigente.lstTaxaPontoCurvaVigente[i].CodigoTipoPontoCurva.Equals(TIPO_PONTO_INTERPOLACAO))
                    {
                        interpolacaoID = Curva.CurvaVigente.lstTaxaPontoCurvaVigente[i].CodigoTipoTaxa;
                    }
                    else if (Curva.CurvaVigente.lstTaxaPontoCurvaVigente[i].CodigoTipoPontoCurva.Equals(TIPO_PONTO_EXTRAPOLACAO))
                    {
                        extrapolacaoID = Curva.CurvaVigente.lstTaxaPontoCurvaVigente[i].CodigoTipoTaxa;
                    }
                    else if (Curva.CurvaVigente.lstTaxaPontoCurvaVigente[i].CodigoTipoPontoCurva.Equals(TIPO_PONTO_VERTICE))
                    {
                        tipoVerticeInterpolacoID = Curva.CurvaVigente.lstTaxaPontoCurvaVigente[i].CodigoTipoTaxa;
                    }
                }

                var tipoPrazo = (from t in tiposPrazo
                                 where t.Codigo == Curva.CurvaVigente.CodigoTipoPrazo
                                 select t).ToList();

                if (tipoPrazo.Count > 0)
                {
                    baseDias = tipoPrazo[0].QuantidadeDiasAno;
                }

                IList<CurvaExecucaoPonto> listaPontos = null;
                Interpolador interpolador = new Interpolador();

                double taxaVerticeZero = 0;

                if (tipoVerticeInterpolacoID == TIPO_TAXA_FATOR_DIARIO && Curva.CurvaVigente.IndicadorPrimeiroFator1)
                {
                    taxaVerticeZero = 1;
                    
                    CurvaExecucaoPonto primeiroPonto = listaVertices.FirstOrDefault(x => x.QuantidadeDiasCorridos == 0);

                    if (primeiroPonto == null 
                        || (primeiroPonto != null && primeiroPonto.CodigoCurva == 0))
                    {
                        primeiroPonto = new CurvaExecucaoPonto();
                        listaVertices.Add(primeiroPonto);
                    }

                    primeiroPonto.CodigoCurva = RegistroExecucaoCurva.CodigoCurva;
                    primeiroPonto.DataHoraExecucaoCurva = RegistroExecucaoCurva.DataHoraExecucaoCurva;
                    primeiroPonto.DataInclusaoVigencia = RegistroExecucaoCurva.DataInclusaoVigencia;
                    primeiroPonto.DataVencimento = RegistroExecucaoCurva.DataHoraExecucaoCurva.Date;
                    primeiroPonto.ValorVertice = taxaVerticeZero;
                    primeiroPonto.QuantidadeDiasCorridos = 0;
                    primeiroPonto.QuantidadeDiasUteis = 0;
                    primeiroPonto.IndicadorVertice = INDICADOR_VERTICE_NAO;
                }
                else
                {
                    taxaVerticeZero = 0;
                }

                try
                {
                    IEnumerable<CurvaExecucaoPonto> EnumList = listaVertices.OrderBy(f => f.QuantidadeDiasCorridos);
                    listaVertices = EnumList.ToList();

                    listaPontos = interpolador.Interpola(listaVertices, taxaVerticeZero, RegistroExecucaoCurva.DataHoraExecucaoCurva, dataMaxGeracaoCurva, antepolacaoID, interpolacaoID, extrapolacaoID, baseDias, tipoVerticeInterpolacoID);

                    interpolador.AjustarTaxaParaPontosEmDiasNaoUteis(ref listaPontos, Curva.CurvaVigente.CodigoModeloCalculo, tipoVerticeInterpolacoID, interpolacaoID, baseDias);
                }
                catch (Exception ex)
                {
                    Exception exCustom = new Exception(string.Format("Falha no cálculo de interpolação, na curva de código: {0} e nome: {1}.", RegistroExecucaoCurva.CodigoCurva, Curva.NomeCurva), ex);
                    EnterpriseLibraryFactory.GetExceptionHandler().HandleException(exCustom, "ErrosDados");
                }

                taxa = Curva.CurvaVigente.lstTaxaPontoCurvaVigente.Where(c => c.CodigoTipoPontoCurva == TIPO_PONTO_VERTICE).ToList();

                if (listaPontos != null && listaPontos.Count > 0)
                {
                    execucaoCurvaBS.CriarCurvaExecucaoPontos(listaPontos, System.Configuration.ConfigurationManager.AppSettings["Procedure"]);
                }

                bool valido = ValidarTunelamento();
                execucaoCurvaBS.ValidarLiberacaoCurva(RegistroExecucaoCurva, Curva, valido);

                execucaoCurvaBS.RemoverCurvaExecucaoPontos(RegistroExecucaoCurva, 1);
                execucaoCurvaBS.RemoverCurvaExecucaoPontos(RegistroExecucaoCurva, 0);
            }
        }

        private bool ValidarTunelamento()
        {
            return execucaoCurvaBS.ValidarCurvaExecucao(Curva, RegistroExecucaoCurva);
        }

        private IList<TaxaPontoCurvaVigente> ConverterVertices(IConversorTaxas conversor, IList<TipoPrazo> tiposPrazo)
        {
            IList<TaxaPontoCurvaVigente> taxa;

            taxa = Curva.CurvaVigente.lstTaxaPontoCurvaVigente.Where(c => c.CodigoTipoTaxa != Curva.CurvaVigente.CodigoTipoTaxa && c.CodigoTipoPontoCurva == TIPO_PONTO_VERTICE).ToList();

            return taxa;
        }

        private void ConverterVerticesInclusaoManual(IConversorTaxas conversor)
        {
            if (Curva.CurvaVigente.CodigoTipoEntradaDado == CURVA_TIPO_ENTRADA_MANUAL && Curva.CurvaVigente.lstTaxaPontoCurvaVigente.Where(c => c.CodigoTipoPontoCurva == TIPO_PONTO_ANTEPOLACAO && c.CodigoTipoTaxa != TIPO_TAXA_FATOR_DIARIO).ToList().Count > 0)
            {
                listaVertices = conversor.ConverteFatoresDiariosParaPU(listaVertices);
            }
        }

        private IList<CurvaExecucaoPonto> CalcularCurvaComposta(ComposicaoCurva composicaoCurvaComposta,
                                                                int prazoCurvaComposta,
                                                                IList<CurvaVigente> listaCurvasFatorVigenteComPontos,
                                                                CurvaVigente curvaVigenteComposta,
                                                                RegistroExecucaoCurva registroExecucaoCurvaComposta)
        {
            ComposicaoCurvaComposta = composicaoCurvaComposta;
            ListaCurvasFatorVigenteComPontos = listaCurvasFatorVigenteComPontos;

            Dictionary<string, string> expressoes = new Dictionary<string, string>();

            string Formula = RemoverParentesesDesnecessarios(composicaoCurvaComposta.FormulaComposicao);

            string expressao = string.Empty;
            char[] arrChar = Formula.ToCharArray();
            char aux;
            int indice = 0;

            while (Formula.IndexOf('(') > -1)
            {
                aux = arrChar[indice];

                switch (aux)
                {
                    case '(':
                        expressao = string.Empty;
                        expressao += aux;
                        break;

                    case ')':
                        expressao += aux;
                        Formula = Formula.Replace(expressao, "L" + expressoes.Count);
                        expressoes.Add("L" + expressoes.Count, expressao);
                        arrChar = Formula.ToCharArray();
                        indice = 0;
                        break;

                    default:
                        expressao += aux;
                        break;
                }

                if (aux != ')')
                    indice += 1;
            }

            expressoes.Add("L" + expressoes.Count, Formula);

            return TratarExpressao(expressoes);
        }

        private string RemoverParentesesDesnecessarios(string Formula)
        {
            Regex reg = new Regex("(\\(([0-9]{1,3}|[a-z]{1,2}|[A-Z]{1,2})\\))");

            MatchCollection mc = reg.Matches(Formula);

            if (mc.Count > 0)
            {
                Formula = reg.Replace(Formula, "|");

                string[] pedacos = Formula.Split('|');
                string resultado = string.Empty;

                for (int i = 0; i < mc.Count; i++)
                {
                    if ((i == mc.Count - 1) && (pedacos.Length > mc.Count))
                    {
                        resultado += pedacos[i] + mc[i].Value.Replace("(", string.Empty).Replace(")", string.Empty) + pedacos[i + 1];
                    }
                    else
                    {
                        resultado += pedacos[i] + mc[i].Value.Replace("(", string.Empty).Replace(")", string.Empty);
                    }
                }

                return resultado.Replace(" ", string.Empty);
            }
            else
            {
                return Formula.Replace(" ", string.Empty);
            }
        }

        private object RetornarSubResultado(string c, Dictionary<string, object> dicAux)
        {
            object retorno = null;

            if (!(c.ToUpper().Equals("DC") || c.ToUpper().Equals("DU")))
            {
                IList<ItemComposicao> composicao = ComposicaoCurvaComposta.Itens.Where(i => i.OperandoFormula == c).ToList<ItemComposicao>();

                if (composicao != null && composicao.Count() > 0)
                {
                    ItemComposicao i = composicao[0];
                    IList<CurvaVigente> curvaVigente = ListaCurvasFatorVigenteComPontos.Where(curva => curva.CodigoCurvaVigente == i.CodigoCurvaIntegrante).ToList<CurvaVigente>();

                    if (curvaVigente != null && curvaVigente.Count() > 0)
                    {
                        IList<RegistroExecucaoCurva> registroExecucao = curvaVigente[0].RegistroExecucaoCurva.OrderByDescending(r => r.DataHoraLiberacaoCurva).ToList<RegistroExecucaoCurva>();
                        retorno = registroExecucao[0].CurvaExecucaoPonto.OrderBy(p => p.DataVencimento).ToList<CurvaExecucaoPonto>();
                    }
                }
                else
                {
                    if (dicAux.ContainsKey(c))
                    {
                        retorno = dicAux[c];
                    }
                }
            }
            else
            {
                ItemComposicao i = ComposicaoCurvaComposta.Itens[0];

                IList<CurvaVigente> curvaVigente = ListaCurvasFatorVigenteComPontos.Where(curva => curva.CodigoCurvaVigente == i.CodigoCurvaIntegrante).ToList<CurvaVigente>();

                if (curvaVigente != null && curvaVigente.Count() > 0)
                {
                    IList<RegistroExecucaoCurva> registroExecucao = curvaVigente[0].RegistroExecucaoCurva.OrderByDescending(r => r.DataHoraLiberacaoCurva).ToList<RegistroExecucaoCurva>();
                    retorno = registroExecucao[0].CurvaExecucaoPonto.OrderBy(p => p.DataVencimento).ToList<CurvaExecucaoPonto>();
                }
            }

            return retorno;
        }

        private IList<CurvaExecucaoPonto> TratarExpressao(Dictionary<string, string> expressoes)
        {
            object result = null;
            Dictionary<string, object> dicResultados = new Dictionary<string, object>();
            Dictionary<string, object> dicSubResultados = new Dictionary<string, object>();

            foreach (string dic in expressoes.Keys)
            {
                string ant = string.Empty;
                string expressao = expressoes[dic];
                string c;
                string nomeValor;
                string nomeValor1;
                IList<string> arrStr = CriarArrayString(expressao);
                string auxStr = string.Empty;
                string chaveDicionario = string.Empty;
                object resultado;
                object resultado1;
                IList<CurvaExecucaoPonto> lista = new List<CurvaExecucaoPonto>();
                IList<CurvaExecucaoPonto> lista1 = new List<CurvaExecucaoPonto>();

                foreach (string key in dicResultados.Keys)
                {
                    dicSubResultados.Add(key, dicResultados[key]);
                }

                for (int i = 0; i < arrStr.Count; i++)
                {
                    c = arrStr[i];
                    auxStr += c;
                    switch (c)
                    {
                        case "*":
                            resultado = RetornarSubResultado(ant, dicSubResultados);
                            resultado1 = RetornarSubResultado(arrStr[i + 1], dicSubResultados);

                            ConverterResultados(resultado, out lista, ant, out nomeValor);
                            ConverterResultados(resultado1, out lista1, arrStr[i + 1], out nomeValor1);

                            if (lista.Count > 0
                                || lista1.Count > 0)
                            {
                                result = execucaoCurvaBS.Multiplicar(lista, lista1, nomeValor, nomeValor1);
                            }
                            else
                            {
                                result = Multiplicar(nomeValor, nomeValor1);
                            }

                            chaveDicionario = "A" + dicSubResultados.Count;
                            dicSubResultados.Add(chaveDicionario, result);
                            expressao = expressao.Replace(ant + c + arrStr[i + 1], chaveDicionario);
                            auxStr = string.Empty;
                            arrStr = CriarArrayString(expressao);
                            i = i - 2;
                            break;
                        case "/":
                            resultado = RetornarSubResultado(ant, dicSubResultados);
                            resultado1 = RetornarSubResultado(arrStr[i + 1], dicSubResultados);

                            ConverterResultados(resultado, out lista, ant, out nomeValor);
                            ConverterResultados(resultado1, out lista1, arrStr[i + 1], out nomeValor1);

                            if (lista.Count > 0
                                || lista1.Count > 0)
                            {
                                result = execucaoCurvaBS.Dividir(lista, lista1, nomeValor, nomeValor1);
                            }
                            else
                            {
                                result = Dividir(nomeValor, nomeValor1);
                            }

                            chaveDicionario = "A" + dicSubResultados.Count;
                            dicSubResultados.Add(chaveDicionario, result);
                            expressao = expressao.Replace(ant + c + arrStr[i + 1], chaveDicionario);
                            auxStr = string.Empty;
                            arrStr = CriarArrayString(expressao);
                            i = i - 2;
                            break;
                        case "+":
                            resultado = RetornarSubResultado(ant, dicSubResultados);
                            resultado1 = RetornarSubResultado(arrStr[i + 1], dicSubResultados);

                            ConverterResultados(resultado, out lista, ant, out nomeValor);
                            ConverterResultados(resultado1, out lista1, arrStr[i + 1], out nomeValor1);

                            if (lista.Count > 0
                                || lista1.Count > 0)
                            {
                                result = execucaoCurvaBS.Somar(lista, lista1, nomeValor, nomeValor1);
                            }
                            else
                            {
                                result = Somar(nomeValor, nomeValor1);
                            }

                            chaveDicionario = "A" + dicSubResultados.Count;
                            dicSubResultados.Add(chaveDicionario, result);
                            expressao = expressao.Replace(ant + c + arrStr[i + 1], chaveDicionario);
                            auxStr = string.Empty;
                            arrStr = CriarArrayString(expressao);
                            i = i - 2;
                            break;
                        case "-":
                            resultado = RetornarSubResultado(ant, dicSubResultados);
                            resultado1 = RetornarSubResultado(arrStr[i + 1], dicSubResultados);

                            ConverterResultados(resultado, out lista, ant, out nomeValor);
                            ConverterResultados(resultado1, out lista1, arrStr[i + 1], out nomeValor1);

                            if (lista.Count > 0
                                || lista1.Count > 0)
                            {
                                result = execucaoCurvaBS.Subtrair(lista, lista1, nomeValor, nomeValor1);
                            }
                            else
                            {
                                result = Subtrair(nomeValor, nomeValor1);
                            }

                            chaveDicionario = "A" + dicSubResultados.Count;
                            dicSubResultados.Add(chaveDicionario, result);
                            expressao = expressao.Replace(ant + c + arrStr[i + 1], chaveDicionario);
                            auxStr = string.Empty;
                            arrStr = CriarArrayString(expressao);
                            i = i - 2;
                            break;
                        case "^":
                            resultado = RetornarSubResultado(ant, dicSubResultados);
                            resultado1 = RetornarSubResultado(arrStr[i + 1], dicSubResultados);

                            ConverterResultados(resultado, out lista, ant, out nomeValor);
                            ConverterResultados(resultado1, out lista1, arrStr[i + 1], out nomeValor1);

                            if (lista.Count > 0
                                || lista1.Count > 0)
                            {
                                result = execucaoCurvaBS.Potencia(lista, lista1, nomeValor, nomeValor1);
                            }
                            else
                            {
                                result = Potencia(nomeValor, nomeValor1);
                            }

                            chaveDicionario = "A" + dicSubResultados.Count;
                            dicSubResultados.Add(chaveDicionario, result);
                            expressao = expressao.Replace(ant + c + arrStr[i + 1], chaveDicionario);
                            auxStr = string.Empty;
                            arrStr = CriarArrayString(expressao);
                            i = i - 2;
                            break;
                    }

                    if (c != "*" && c != "/" && c != "-" && c != "+" && c != "^")
                    {
                        ant = c;
                    }
                }

                dicResultados.Add(dic, dicSubResultados.ElementAt(dicSubResultados.Count - 1).Value);
                dicSubResultados.Clear();
            }

            return (IList<CurvaExecucaoPonto>)result;
        }

        private void ConverterResultados(object resultado, out IList<CurvaExecucaoPonto> lista, string ant, out string nomeValor)
        {
            nomeValor = ant.ToUpper();
            lista = new List<CurvaExecucaoPonto>();

            if (resultado != null)
            {
                if (resultado.GetType() == typeof(List<CurvaExecucaoPonto>))
                {
                    lista = (IList<CurvaExecucaoPonto>)resultado;
                }
                else
                {
                    nomeValor = resultado.ToString();
                }
            }
        }

        public double Multiplicar(string valor, string valor2)
        {
            return Double.Parse(valor) * Double.Parse(valor2);
        }

        public double Dividir(string valor, string valor2)
        {
            return (double)Double.Parse(valor) / Double.Parse(valor2);
        }

        public double Somar(string valor, string valor2)
        {
            return Double.Parse(valor) + Double.Parse(valor2);
        }

        public double Subtrair(string valor, string valor2)
        {
            return Double.Parse(valor) - Double.Parse(valor2);
        }

        public double Potencia(string valor, string valor2)
        {
            return Math.Pow(Double.Parse(valor), Double.Parse(valor2));
        }

        public double ConverterValorParaDouble(string valor) 
        {
            if (valor.Contains("#"))
            {
                return Convert.ToDouble(valor.Replace("#", "-"));
            }
            else 
            {
                return Convert.ToDouble(valor);   
            }
        }

        private IList<string> CriarArrayString(string entrada)
        {
            IList<string> retorno = new List<string>();

            string aux = string.Empty;

            foreach (char c in entrada.ToCharArray())
            {
                switch (c)
                {
                    case '+':
                    case '-':
                    case '*':
                    case '/':
                    case '^':
                        retorno.Add(aux);
                        retorno.Add(c.ToString());
                        aux = string.Empty;
                        break;
                    case '(':
                    case ')':
                        break;

                    default:
                        aux += c.ToString();
                        break;
                }
            }
            retorno.Add(aux);
            return retorno;
        }

        private void TratarPosGeracao()
        {
            RenomearArquivo();
        }

        private void RenomearArquivo()
        {
            try
            {
                if (Curva.CurvaVigente.CodigoTipoEntradaDado == CURVA_TIPO_ENTRADA_IMPORTACAO_ARQUIVO 
                    && importArquivo != null
                    && importArquivo.IndicadorValidacaoArquivo)
                {
                    string dataAtual = DateTime.Now.ToString("ddMMyyyy_HHmmss");
                    string nomeArquivo = importArquivo.NomeArquivo;
                    string extensao = importArquivo.Sigla;
                    string novoNome = null;

                    novoNome = importArquivo.NomeArquivo.Split('.')[0] + "_" + dataAtual + "." + extensao;

                    importArquivoVertices.RenomearArquivo(importArquivo, novoNome);
                }
            }
            catch (Exception ex)
            {
                Exception exCustom = new Exception(string.Format("Não foi possível renomear o arquivo para curva de código: {0} e nome: {1}.", RegistroExecucaoCurva.CodigoCurva, Curva.NomeCurva), ex);
                EnterpriseLibraryFactory.GetExceptionHandler().HandleException(exCustom, "ErrosDados");
            }
        }

        private void VerificarDataBaseArquivo()
        {
            if (Curva.CurvaVigente.CodigoTipoEntradaDado == CURVA_TIPO_ENTRADA_IMPORTACAO_ARQUIVO &&
                importArquivo.IndicadorValidacaoData)
            {
                DateTime dataBase = importArquivoVertices.LerDataBaseArquivo(importArquivo);

                if (!dataBase.Date.Equals(RegistroExecucaoCurva.DataHoraExecucaoCurva.Date))
                {
                    Exception exCustom = new Exception(string.Format("Data base Inválida. Arquivo:({0}) Curva:({1}-{2}).", importArquivo.NomeArquivo, RegistroExecucaoCurva.CodigoCurva, Curva.NomeCurva), null);
                    throw exCustom;
                }
            }
        }

        #endregion

    }
}
